﻿using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Kylin
{
    public class NetworkImplement
    {
        private TcpClient client;
        private string host;
        private int port;
        private Stream stream;
        private bool closed;

        public NetworkImplement(string host, int port)
        {
            ConnectTimeout = 3000;
            this.host = host;
            this.port = port;
        }

        internal class AsyncResult : IAsyncResult
        {
            internal AsyncResult(AsyncCallback callback, object state, NetworkImplement socket)
            {
                this.Socket = socket;
                this.Callback = callback;
                this.AsyncState = state;
                this.IsCompleted = false;
                this.AsyncWaitHandle = new ManualResetEvent(false);
            }

            internal AsyncCallback Callback { get; set; }
            public NetworkImplement Socket { get; internal set; }
            public object AsyncState { get; internal set; }
            public WaitHandle AsyncWaitHandle { get; internal set; }

            public bool CompletedSynchronously
            {
                get { return false; }
            }

            public bool IsCompleted { get; internal set; }
            internal int ReadCount { get; set; }
            internal Exception Error { get; set; }

            internal int Wait()
            {
                AsyncWaitHandle.WaitOne();
                if (Error != null)
                    throw Error;
                return ReadCount;
            }
        }

        public IAsyncResult BeginConnect(string host, int port, AsyncCallback callback, object state)
        {
            if (client != null)
                throw new InvalidOperationException();

            AsyncResult ar1 = new AsyncResult(callback, state, this);
            ThreadPool.QueueUserWorkItem((object ar2) =>
            {
                AsyncResult ar3 = (AsyncResult) ar2;
                try
                {
                    Connect(host, port);
                }
                catch (Exception ex)
                {
                    ar3.Error = ex;
                }
                ar3.IsCompleted = true;
                ((ManualResetEvent) ar3.AsyncWaitHandle).Set();
                if (ar3.Callback != null)
                    ar3.Callback(ar3);
            }, ar1);

            return ar1;
        }

        public void WaitConnect(IAsyncResult asyncResult)
        {
            ((AsyncResult) asyncResult).Wait();
        }

        public void EndConnect(IAsyncResult asyncResult)
        {
            ((AsyncResult) asyncResult).Wait();
        }

        public void Connect(string host, int port)
        {
            if (client != null)
                throw new InvalidOperationException();

            client = new TcpClient();
            var ar = client.BeginConnect(host, port, null, null);
            ar.AsyncWaitHandle.WaitOne(new TimeSpan(0, 0, 0, 0, ConnectTimeout));
            if (!ar.IsCompleted)
            {
                throw new TimeoutException();
            }
            client.EndConnect(ar);
            stream = client.GetStream();
        }

        public bool isAllZero(byte[] b, int offset, int length)
        {
            for (int i = offset; i < offset + length; i++)
            {
                if (b[i] != 0)
                {
                    return false;
                }
            }
            return true;
        }

        public String toString(byte[] bb)
        {
            var sb = new StringBuilder();
            for (var i = 0; i < bb.Length; i++)
            {
                var b = bb[i];
                sb.Append(b);
                if (i < bb.Length - 1)
                {
                    sb.Append(", ");
                }
            }
            return sb.ToString();
        }

        public IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            AsyncResult ar1 = new AsyncResult(callback, state, this);
            ThreadPool.QueueUserWorkItem((object ar2) =>
            {
                AsyncResult ar3 = (AsyncResult) ar2;
                try
                {
                    while (ar3.ReadCount != count)
                    {
                        ar3.ReadCount += stream.Read(buffer, offset + ar3.ReadCount, count - ar3.ReadCount);
                    }
                }
                catch (Exception ex)
                {
                    ar3.Error = ex;
                }
                ar3.IsCompleted = true;
                ((ManualResetEvent) ar3.AsyncWaitHandle).Set();
                if (ar3.Callback != null)
                    ar3.Callback(ar3);
            }, ar1);
            return ar1;
        }

        public int EndRead(IAsyncResult asyncResult)
        {
            return ((AsyncResult) asyncResult).Wait();
        }

        public IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback,
            object state)
        {
            AsyncResult ar1 = new AsyncResult(callback, state, this);
            ThreadPool.QueueUserWorkItem((object ar2) =>
            {
                AsyncResult ar3 = (AsyncResult) ar2;
                try
                {
                    stream.Write(buffer, offset, count);
                }
                catch (Exception ex)
                {
                    ar3.Error = ex;
                }
                ar3.IsCompleted = true;
                ((ManualResetEvent) ar3.AsyncWaitHandle).Set();
                if (ar3.Callback != null)
                    ar3.Callback(ar3);
            }, ar1);
            return ar1;
        }

        public void EndWrite(IAsyncResult asyncResult)
        {
            ((AsyncResult)asyncResult).Wait();
        }

        public void Close()
        {
            closed = true;
			if (stream != null) {
				stream.Close ();
			}
			if (client != null) {
				client.Close ();
			}
        }

        public int ConnectTimeout { get; set; }

        private int HandShakeTimeout { get; set; }

        private int _ReadTimeout;
        public int ReadTimeout
        {
            get { return _ReadTimeout; }
            set
            {
                _ReadTimeout = value;
                if (stream != null)
                    stream.ReadTimeout = value;
            }
        }

        private int _WriteTimeout;
        public int WriteTimeout
        {
            get { return _WriteTimeout; }
            set
            {
                _WriteTimeout = value;
                if (stream != null)
                    stream.WriteTimeout = value;
            }
        }
    }
}

